using System;
using System.Linq;
using LoggingMagic.AssemlyOperations;
using LoggingMagic.Injectors.Helpers;
using LoggingMagic.Injectors.Interfaces;
using Mono.Cecil;
using LoggingMagic.Extensions;

namespace LoggingMagic.Injectors.StaticMethod
{
    internal class StaticMethodCallInjector : IInjector
    {
        private readonly ModuleDefinition _module;
        private readonly MethodParametersToArray _parametersToArray;

        public StaticMethodCallInjector(ModuleDefinition module, MethodParametersToArray parametersToArray)
        {
            _module = module;
            _parametersToArray = parametersToArray;
        }



        public void Inject(MethodDefinition method, Instructions ops)
        {
            if (SkipMethod(method))
                return;

            var parameters = _parametersToArray.Convert(method, ops);

            #region Log.Entering(fullName, parameters)

            ops.Ldstr(method.MethodName());
            ops.Ldloc(parameters);
            ops.Call(LoggingMethod);

            #endregion
        }

        /// <summary>
        /// Skip some methods to prevent stack overflow
        /// </summary>
        protected bool SkipMethod(MethodDefinition method)
        {
            return method == LoggingMethod;
        }

        private MethodDefinition _loggingMethod;
        private MethodDefinition LoggingMethod
        {
            get
            {
                if (_loggingMethod == null)
                {
                    _loggingMethod = FindLoggingMethod();
                }

                return _loggingMethod;
            }
        }

        private MethodDefinition FindLoggingMethod()
        {
            var loggingClass = _module.Types.Where(x => x.Name == "Logger" && x.IsClass).First();

            var method = loggingClass.Methods.Where(x => x.IsStatic && x.Name == "Entering" &&
                x.ReturnType.FullName == "System.Void" &&
                x.Parameters.Count == 2 &&
                x.Parameters[0].ParameterType.Name == "String").First();

            return method;
        }
    }
}